package com.soyea.service;

import com.google.common.base.Preconditions;
import com.soyea.common.OperateConstant;
import com.soyea.common.RequestHolder;
import com.soyea.common.ResultEnum;
import com.soyea.dao.SysDeptMapper;
import com.soyea.dao.SysUserMapper;
import com.soyea.exception.ParamException;
import com.soyea.model.SysDept;
import com.soyea.param.DeptParam;
import com.soyea.util.BeanValidator;
import com.soyea.util.IpUtil;
import com.soyea.util.LevelUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * 部门服务
 * Created by skh on 2018/1/3
 */
@Service
@Transactional
public class SysDeptService {

	@Autowired
	private SysDeptMapper sysDeptMapper;

	@Resource
	private SysUserMapper sysUserMapper;

	@Resource
	private SysLogService sysLogService;

	/**
	 * 新增部门
	 *
	 * 思路:
	 *		校验参数
	 * 		检查同一级部门名称是否相同
	 * 		根据parentId,计算新部门的level.计算规则:level=父级的level+子级的parentId
	 * 		补全新部门属性
	 * 	 	执行新增操作
	 * 	 	保存日志
	 *
	 * @param deptParam
	 */
	public void save(DeptParam deptParam) {
		//校验参数
		BeanValidator.check(deptParam);

		//检查同一级部门名称是否相同
		boolean exist = checkExist(deptParam.getParentId(), deptParam.getName(), deptParam.getId());
		if (exist) {
			throw new ParamException(ResultEnum.EXIST_SAME_DEPTNAME.getMessage());
		}

		//填充数据 方式一:使用builder
//		SysDept sysDept = SysDept.builder().name(deptParam.getName()).seq(deptParam.getSeq()).parentId(deptParam.getParentId()).remark(deptParam.getRemark()).build();

		//方式二:使用spring的BeanUtils
		SysDept sysDept = new SysDept();
		BeanUtils.copyProperties(deptParam, sysDept);

		//计算level
		String parentLevel = getParentLevel(deptParam.getParentId());
		String level = LevelUtil.calculateLevel(parentLevel, deptParam.getParentId());
		sysDept.setLevel(level);

		sysDept.setOperateIp(IpUtil.getUserIP(RequestHolder.getCurrentRequest()));
		sysDept.setOperateTime(OperateConstant.operateTime);
		sysDept.setOperator(RequestHolder.getCurrentUser().getUsername());

		//新增
		sysDeptMapper.insertSelective(sysDept);

		sysLogService.saveDeptLog(null, sysDept);

	}

	/**
	 * 修改部门
	 *
	 * 注意点:如果修改了parentId则需要同时修改子部门,因为子部门的level跟parentId有关,所以发生了变化
	 *
	 * 思路:
	 * 		校验参数
	 * 		检查同一级部门名称是否相同
	 * 		根据id,查询数据库中的部门信息,判断部门是否存在
	 * 		重新计算Level,封装修改后的部门属性
	 * 		修改当前部门及子部门
	 * 		保存日志
	 *
	 * @param deptParam
	 */
	public void update(DeptParam deptParam) {
		//校验参数
		BeanValidator.check(deptParam);

		//检查同一级部门名称是否相同
		boolean exist = checkExist(deptParam.getParentId(), deptParam.getName(), deptParam.getId());
		if (exist) {
			throw new ParamException(ResultEnum.EXIST_SAME_DEPTNAME.getMessage());
		}

		//查询部门是否存在
		SysDept before = sysDeptMapper.selectByPrimaryKey(deptParam.getId());
		Preconditions.checkNotNull(before, ResultEnum.DEPT_NOT_EXIST.getMessage(), deptParam.getId());

		//封装修改后的部门
		SysDept after = SysDept.builder().id(deptParam.getId()).name(deptParam.getName()).parentId(deptParam.getParentId())
				.seq(deptParam.getSeq()).remark(deptParam.getRemark()).build();
		after.setLevel(LevelUtil.calculateLevel(getParentLevel(deptParam.getParentId()), deptParam.getParentId()));


		after.setOperateIp(IpUtil.getUserIP(RequestHolder.getCurrentRequest()));
		after.setOperateTime(OperateConstant.operateTime);
		after.setOperator(RequestHolder.getCurrentUser().getUsername());

		//修改当前部门和子部门
		updateWithChild(before, after);

		sysLogService.saveDeptLog(before, after);


	}

	/**
	 * 查询指定部门详情
	 *
	 * 思路:
	 * 根据id,查询部门
	 *
	 * @param id
	 * @return
	 */
	public SysDept showDetail(Integer id) {
		SysDept sysDept = sysDeptMapper.selectByPrimaryKey(id);
		return sysDept;
	}

	/**
	 * 删除部门
	 *
	 * 思路:
	 * 根据部门id,查询出该部门
	 * 判断该部门是否存在
	 * 判断该部门下是否有子部门
	 * 判断该部门下是否有用户
	 * 都通过则删除该部门
	 *
	 * @param deptId
	 */
	public void delete(int deptId) {
		SysDept dept = sysDeptMapper.selectByPrimaryKey(deptId);
		Preconditions.checkNotNull(dept, "待删除的部门不存在，无法删除",deptId);
		if (sysDeptMapper.countByParentId(dept.getId()) > 0) {
			throw new ParamException("当前部门下面有子部门，无法删除");
		}
		if(sysUserMapper.countByDeptId(dept.getId()) > 0) {
			throw new ParamException("当前部门下面有用户，无法删除");
		}
		sysDeptMapper.deleteByPrimaryKey(deptId);
	}


	/**
	 * 检查同一级部门名称是否相同
	 * 注意点:新增时不用传deptId,修改时需要传deptId
	 * 因为修改需要排除当前部门的名称 通过id!=#{deptId}
	 */
	public boolean checkExist(Integer parentId, String name, Integer deptId) {
		return sysDeptMapper.countByNameAndParentId(parentId, name, deptId) > 0;
	}


	/**
	 * 获取父部门等级level
	 *
	 * 思路:
	 * 根据parentId查询出父部门
	 * 判断父部门是否存在
	 * 返回父部门的level
	 *
	 */
	public String getParentLevel(Integer parentId) {
		//查询父部门
		SysDept parentDept = sysDeptMapper.selectByPrimaryKey(parentId);
		if (parentDept == null) {
			return null;
		}
		return parentDept.getLevel();
	}

	/**
	 * 修改当前部门和子部门的level
	 *
	 * 注意点:如果更新前后的level没有发生变化,则不需要更新子部门.
	 *
	 * 思路:
	 * 获取更新前和更新后的level值
	 * 判断level是否发生改变,如果改变,则需要修改子部门,否则不修改子部门
	 * 获取更新前部门的所有子部门列表,根据level值,模糊查询得到
	 * 判断子部门列表是否为空,遍历子部门列表,修改每个子部门的level
	 * 计算每个子部门的level值,计算规则:新的level = 更新后的部门level+截取旧level后面的字符串
	 * 修改子部门的level值
	 * 退出遍历,批量修改子部门列表
	 * 最后,更新当前部门
	 *
	 */
	public void updateWithChild(SysDept before, SysDept after) {
		String newLevelPrefix = after.getLevel();
		String oldLevelPrefix = before.getLevel();
		//如果更新前后的level没有发生变化,则不需要更新子部门.
		if (!after.getLevel().equals(before.getLevel())) {
			//获取更新前部门的所有子部门列表
			String childLevel = new StringBuilder().append(before.getLevel()).append(".").append(before.getId()).toString();
			String childLevelLike = childLevel.concat(".%");
			List<SysDept> deptList = sysDeptMapper.getChildDeptListByLevel(childLevel,childLevelLike);
			if (CollectionUtils.isNotEmpty(deptList)) {
				//遍历子部门列表,修改level
				for (SysDept dept : deptList) {
					String level = dept.getLevel();
					if (level.indexOf(oldLevelPrefix) == 0) {
						//如果子部门的level是以更新前的部门level开头,则更新子部门level.
						//新的level = 更新后的部门level+截取旧level后面的字符串
						level = newLevelPrefix + level.substring(oldLevelPrefix.length());
						dept.setLevel(level);
						//0,1  0,1,2 0,1,3
						//0,3  0,3,2 0,3,3
					}
				}
				//批量更新子部门
				sysDeptMapper.batchUpdateLevel(deptList);
			}
		}
		//更新当前部门.
		sysDeptMapper.updateByPrimaryKey(after);

	}
}
